import java.math.BigInteger;

public final class Packet extends NodeSub {
	public byte[] payload;
	public int offset;
	public int bitPos;
	private static int[] packetSizes = new int[256];
	private static final int[] bitMasks;
	public IsaacCipher packetCryption;
	private static int anInt1459;
	private static int anInt1460;
	private static int anInt1461;
	private static Deque aDeque_1462;
	private static Deque aDeque_1463;
	private static Deque aDeque_1464;

	public static Packet create(int size) {
		synchronized (Packet.aDeque_1463) {
			Packet packet = null;
			if (size == 0 && Packet.anInt1459 > 0) {
				Packet.anInt1459--;
				packet = (Packet) Packet.aDeque_1462.popFront();
			} else if (size == 1 && Packet.anInt1460 > 0) {
				Packet.anInt1460--;
				packet = (Packet) Packet.aDeque_1463.popFront();
			} else if (size == 2 && Packet.anInt1461 > 0) {
				Packet.anInt1461--;
				packet = (Packet) Packet.aDeque_1464.popFront();
			}
			if (packet != null) {
				packet.offset = 0;
				return packet;
			}
		}
		Packet packet = new Packet();
		packet.offset = 0;
		if (size == 0) {
			packet.payload = new byte[100];
		} else if (size == 1) {
			packet.payload = new byte[5000];
		} else {
			packet.payload = new byte[30000];
		}
		return packet;
	}

	private Packet() {
	}

	public Packet(byte[] buf) {
		payload = buf;
		offset = 0;
	}

	public void putOpcode(int opcode) {
		payload[offset++] = (byte) (opcode + packetCryption.getNextKey());
	}

	public void putByte(int val) {
		payload[offset++] = (byte) val;
	}

	public void putShort(int val) {
		payload[offset++] = (byte) (val >> 8);
		payload[offset++] = (byte) val;
	}

	public void putLEShort(int val) {
		payload[offset++] = (byte) val;
		payload[offset++] = (byte) (val >> 8);
	}

	public void put24BitInt(int val) {
		payload[offset++] = (byte) (val >> 16);
		payload[offset++] = (byte) (val >> 8);
		payload[offset++] = (byte) val;
	}

	public void putInt(int val) {
		payload[offset++] = (byte) (val >> 24);
		payload[offset++] = (byte) (val >> 16);
		payload[offset++] = (byte) (val >> 8);
		payload[offset++] = (byte) val;
	}

	public void putLEInt(int val) {
		payload[offset++] = (byte) val;
		payload[offset++] = (byte) (val >> 8);
		payload[offset++] = (byte) (val >> 16);
		payload[offset++] = (byte) (val >> 24);
	}

	public void putLong(long val) {
		payload[offset++] = (byte) (int) (val >> 56);
		payload[offset++] = (byte) (int) (val >> 48);
		payload[offset++] = (byte) (int) (val >> 40);
		payload[offset++] = (byte) (int) (val >> 32);
		payload[offset++] = (byte) (int) (val >> 24);
		payload[offset++] = (byte) (int) (val >> 16);
		payload[offset++] = (byte) (int) (val >> 8);
		payload[offset++] = (byte) (int) val;
	}

	public void putString(String string) {
		// string.getBytes(0, string.length(), payload, offset);
		// ^ Deprecated
		System.arraycopy(string.getBytes(), 0, payload, offset, string.length());
		offset += string.length();
		payload[offset++] = (byte) 10;
	}

	public void putBytes(byte[] buf, int len, int off) {
		for (int i = off; i < off + len; i++) {
			payload[offset++] = buf[i];
		}
	}

	public void putSizeByte(int val) {
		payload[offset - val - 1] = (byte) val;
	}

	public int getUByte() {
		return payload[offset++] & 0xff;
	}

	public byte getByte() {
		return payload[offset++];
	}

	public int getUShort() {
		offset += 2;
		return (((payload[offset - 2] & 0xff) << 8) + (payload[offset - 1] & 0xff));
	}

	public int getShort() {
		offset += 2;
		int i = (((payload[offset - 2] & 0xff) << 8) + (payload[offset - 1] & 0xff));
		if (i > 32767) {
			i -= 65536;
		}
		return i;
	}

	public int get24BitInt1() {
		offset += 3;
		return (((payload[offset - 3] & 0xff) << 16)
			+ ((payload[offset - 2] & 0xff) << 8) + (payload[offset - 1] & 0xff));
	}

	public int getInt() {
		offset += 4;
		return (((payload[offset - 4] & 0xff) << 24)
			+ ((payload[offset - 3] & 0xff) << 16)
			+ ((payload[offset - 2] & 0xff) << 8) + (payload[offset - 1] & 0xff));
	}

	public long getLong() {
		long l1 = getInt() & 0xffffffffL;
		long l2 = getInt() & 0xffffffffL;
		return (l1 << 32) + l2;
	}

	public String getString() {
		int i = offset;
		while (payload[offset++] != 10) {
		}
		return new String(payload, i, offset - i - 1);
	}

	public byte[] getBytes() {
		int off = offset;
		while (payload[offset++] != 10) {
		}
		byte[] buf = new byte[offset - off - 1];
		for (int i = off; i < offset - 1; i++) {
			buf[i - off] = payload[i];
		}
		return buf;
	}

	public void getBytes(int len, int off, byte[] buf) {
		for (int i = off; i < off + len; i++) {
			buf[i] = payload[offset++];
		}
	}

	public void initBitAccess() {
		bitPos = offset * 8;
	}

	public int readBits(int bitOff) {
		int off = bitPos >> 3;
		int max_bit_len = 8 - (bitPos & 0x7);
		int dest = 0;
		bitPos += bitOff;
		for (; bitOff > max_bit_len; max_bit_len = 8) {
			dest += ((payload[off++] & Packet.bitMasks[max_bit_len]) << bitOff
				- max_bit_len);
			bitOff -= max_bit_len;
		}
		if (bitOff == max_bit_len) {
			dest += payload[off] & Packet.bitMasks[max_bit_len];
		} else {
			dest += (payload[off] >> max_bit_len - bitOff & Packet.bitMasks[bitOff]);
		}
		return dest;
	}

	public void finishBitAccess() {
		offset = (bitPos + 7) / 8;
	}

	public int getUSmart() {
		int i = payload[offset] & 0xff;
		if (i < 128) {
			return getUByte() - 64;
		}
		return getUShort() - 49152;
	}

	public int getSmart() {
		int i = payload[offset] & 0xff;
		if (i < 128) {
			return getUByte();
		}
		return getUShort() - 32768;
	}

	public void encodeRsa(BigInteger modulus, BigInteger exponent) {
		int off = offset;
		offset = 0;
		byte[] buf = new byte[off];
		getBytes(off, 0, buf);
		BigInteger bigInt1 = new BigInteger(buf);
		BigInteger bigInt2 = bigInt1.modPow(exponent, modulus);
		byte[] bi2Bytes = bigInt2.toByteArray();
		offset = 0;
		putByte(bi2Bytes.length);
		putBytes(bi2Bytes, bi2Bytes.length, 0);
	}

	public void putByteA(int val) {
		payload[offset++] = (byte) (val + 128);
	}

	public void putByteC(int val) {
		payload[offset++] = (byte) -val;
	}

	public void putByteS(int i) {
			payload[offset++] = (byte) (128 - i);
	}

	public int getUByteA() {
		return payload[offset++] - 128 & 0xff;
	}

	public int getUByteC() {
		return -payload[offset++] & 0xff;
	}

	public int getUByteS() {
		return 128 - payload[offset++] & 0xff;
	}

	public byte getByteA() {
		return (byte) (payload[offset++] - 128);
	}

	public byte getByteC() {
		return (byte) -payload[offset++];
	}

	public byte getByteS() {
		return (byte) (128 - payload[offset++]);
	}

	public void putLEShort_duplicate(int val) {
		payload[offset++] = (byte) val;
		payload[offset++] = (byte) (val >> 8);
	}

	public void putShortA(int val) {
		payload[offset++] = (byte) (val >> 8);
		payload[offset++] = (byte) (val + 128);
	}

	public void putLEShortA(int val) {
		payload[offset++] = (byte) (val + 128);
		payload[offset++] = (byte) (val >> 8);
	}

	public int getULEShort() {
		offset += 2;
		return (((payload[offset - 1] & 0xff) << 8) + (payload[offset - 2] & 0xff));
	}

	public int getULEShortA1() {
		offset += 2;
		return (((payload[offset - 2] & 0xff) << 8) + (payload[offset - 1] - 128 & 0xff));
	}

	public int getULEShortA2() {
		offset += 2;
		return (((payload[offset - 1] & 0xff) << 8) + (payload[offset - 2] - 128 & 0xff));
	}

	public int getLEShort() {
		offset += 2;
		int i = (((payload[offset - 1] & 0xff) << 8) + (payload[offset - 2] & 0xff));
		if (i > 32767) {
			i -= 65536;
		}
		return i;
	}

	public int getUShortA() {
		offset += 2;
		int i = (((payload[offset - 2] & 0xff) << 8) + (payload[offset - 1] - 128 & 0xff));
		if (i > 32767) {
			i -= 65536;
		}
		return i;
	}

	public int get24BitInt2() {
		offset += 3;
		return (((payload[offset - 2] & 0xff) << 16)
			+ ((payload[offset - 3] & 0xff) << 8) + (payload[offset - 1] & 0xff));
	}

	public int getLEInt() {
		offset += 4;
		return (((payload[offset - 1] & 0xff) << 24)
			+ ((payload[offset - 2] & 0xff) << 16)
			+ ((payload[offset - 3] & 0xff) << 8) + (payload[offset - 4] & 0xff));
	}

	public int getInt2() {
		offset += 4;
		return (((payload[offset - 2] & 0xff) << 24)
			+ ((payload[offset - 1] & 0xff) << 16)
			+ ((payload[offset - 4] & 0xff) << 8) + (payload[offset - 3] & 0xff));
	}

	public int getInt1() {
		offset += 4;
		return (((payload[offset - 3] & 0xff) << 24)
			+ ((payload[offset - 4] & 0xff) << 16)
			+ ((payload[offset - 1] & 0xff) << 8) + (payload[offset - 2] & 0xff));
	}

	public void getBytesC(byte[] buf, int off, int len) {
			for (int i = len + off - 1; i >= len; i--) {
				buf[i] = payload[offset++];
			}
	}

	public void getBytesA(byte[] buf, int len, int off) {
		for (int i = off; i < off + len; i++) {
			buf[i] = (byte) (payload[offset++] - 128);
		}
	}
	static {
		for (int i = 0; i < 256; i++) {
			int size = i;
			for (int i_41_ = 0; i_41_ < 8; i_41_++) {
				if ((size & 0x1) == 1) {
					size = size >>> 1 ^ ~0x12477cdf;
				} else {
					size >>>= 1;
				}
			}
			Packet.packetSizes[i] = size;
		}
		bitMasks = new int[] { 0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191,
			16383, 32767, 65535, 131071, 262143, 524287, 1048575, 2097151, 4194303, 8388607,
			16777215, 33554431, 67108863, 134217727, 268435455, 536870911, 1073741823, 2147483647,
			-1 };
		Packet.aDeque_1462 = new Deque(true);
		Packet.aDeque_1463 = new Deque(true);
		Packet.aDeque_1464 = new Deque(true);
	}
}